package com.devops.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.devops.admin.mapper.DOSProjectGroupMapper;
import com.devops.admin.mapper.DOSProjectMapper;
import com.devops.admin.mapper.DOSProjectVersionMapper;
import com.devops.admin.mapper.DOSReleaseMapper;
import com.devops.admin.model.*;
import com.devops.admin.po.*;
import com.devops.admin.service.*;
import com.devops.admin.util.Assert;
import com.devops.admin.util.YamlUtils;
import com.devops.admin.vo.*;
import com.devops.constant.DOSEnvTypeEnum;
import com.devops.constant.DOSRoleEnum;
import com.devops.constant.DevOpsConstant;
import com.devops.shiro.vm.BaseUserVM;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassNem DOSProjectServiceImpl
 * @Description TODO
 * @Author heyabo
 * @Date 2022/3/18 15:32
 * @Version 1.0
 **/
@Service
@Slf4j
public class DOSProjectServiceImpl extends ServiceImpl<DOSProjectMapper, DOSProject> implements DOSProjectService {
    @Autowired
    private DOSProjectModuleService dosProjectModuleService;
    @Autowired
    private DOSProjectUserRelationService dosProjectUserRelationService;
    @Autowired
    private DOSResourceService dosResourceService;
    @Autowired
    private DOSProjectGroupMapper dosProjectGroupMapper;
    @Autowired
    private DOSProjectEnvClusterNamespaceRelationService dosProjectEnvClusterNamespaceRelationService;
    @Autowired
    private DOSProjectVersionMapper dosProjectVersionMapper;
    @Autowired
    private DOSProjectGroupUserRelationService dosProjectGroupUserRelationService;
    @Value("${project.similarity.limit}")
    private Double similarityLimit;
    @Autowired
    private DOSReleaseService dosReleaseService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addProject(DOSProject project) {
        //入参校验
        Assert.isTrueMvc(StrUtil.isNotBlank(project.getProjectName()),"项目名称不能为空");
        Assert.isTrueMvc(project.getGroupId() != null && project.getGroupId() > 0,"项目组ID不能为空");
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        //操作权限校验
        checkProjectWritePermission(user, project.getGroupId());
        //重名校验
        Assert.isTrueMvc(count(Wrappers.<DOSProject>lambdaQuery().eq(DOSProject::getProjectName, project.getProjectName())) == 0, "已存在相同名称的项目");
        // 校验名称的相似度
        if (project.getCheckType() == 0) {
            List<String> projectNames = baseMapper.projectNameSimilarity(project.getProjectName(),similarityLimit);
            Assert.isSimilar(ObjectUtil.isEmpty(projectNames), project.getProjectName()+"与" + StringUtils.join(projectNames, ",") + "相似度较高，是否保存");
        }
        if (save(project)) {
            DOSProjectUserRelation userRelation = new DOSProjectUserRelation();
            userRelation.setUserId(user.getId());
            // 1:项目创建者、2：管理者、3：项目开发者
            userRelation.setUserType(DevOpsConstant.UserType.CREATE);
            userRelation.setProjectId(project.getId());
            return dosProjectUserRelationService.save(userRelation);
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteProjectById(Integer projectId) {
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        //入参校验
        Assert.isTrueMvc(projectId != null && projectId > 0,"项目ID不能为空");
        //操作权限校验
        Integer projectGroupId = getById(projectId).getGroupId();
        Assert.isTrueMvc(projectGroupId != null && projectGroupId > 0,"项目不存在");
        checkProjectWritePermission(user, projectGroupId);
        //查询项目是否已经绑定release
        Assert.isTrue(dosReleaseService.count(Wrappers.<DOSRelease>lambdaQuery().eq(DOSRelease::getProjectId,projectId)) == 0,"项目有关联的release无法删除");
        //删除项目下绑定的模块
        deleteModule(projectId);
        //删除人员与项目关联表信息
        dosProjectUserRelationService.remove(Wrappers.<DOSProjectUserRelation>lambdaQuery().eq(DOSProjectUserRelation::getProjectId,projectId));
        //删除项目与环境集群命名空间的绑定关系
        dosProjectEnvClusterNamespaceRelationService.remove(Wrappers.<DOSProjectEnvClusterNamespaceRelation>lambdaQuery().eq(DOSProjectEnvClusterNamespaceRelation::getProjectId,projectId));
        //删除项目
        return this.removeById(projectId);
    }

    @Override
    public TableResult<DOSProjectPo> getProjects(DOSProjectVo projectVo, DOSPageVo pageVo) {
        Page<DOSProjectPo> page = new Page<>(pageVo.getPageNum(), pageVo.getPageSize());
        TableResult<DOSProjectPo> tableResult = new TableResult<>();

        //获取登录的用户信息
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        List<DOSProjectPo> projectPos;

        if (user.getAdminRole()) {
            projectPos = baseMapper.getAdminProjects(page,projectVo);
        } else {
            projectVo.setUserId(user.getId());
            List<DOSProjectPo> tempPos = baseMapper.getProjects(page, projectVo);
            //处理重复的project数据 这些数据也就 isEdit 不一致
            Map<Integer, List<DOSProjectPo>> map = tempPos.stream().collect(Collectors.groupingBy(DOSProjectPo::getId));
            List<DOSProjectPo> dealPos = new ArrayList<>();
            map.forEach((key,value) -> {
                if (value != null && value.size() > 0) {
                    if (value.size() == 1) {
                        dealPos.addAll(value);
                    } else {
                        DOSProjectPo po = value.get(0);
                        List<Boolean> isEdits = value.stream().map(DOSProjectPo::getIsEdit).collect(Collectors.toList());
                        if (isEdits.contains(Boolean.TRUE)) {
                            po.setIsEdit(Boolean.TRUE);
                        }
                        dealPos.add(po);
                    }
                }
            });
            projectPos = dealPos.stream().sorted(Comparator.comparing(DOSProjectPo::getUpdateTime).reversed()).collect(Collectors.toList());
        }
        //查询版本信息
        List<Integer> versionIds = projectPos.stream().map(DOSProjectPo::getVersionId).collect(Collectors.toList());
        if (versionIds.isEmpty()) {
            return tableResult;
        }
        List<DOSProjectVersion> dosProjectVersions = dosProjectVersionMapper.selectBatchIds(versionIds);
        projectPos.forEach(project -> dosProjectVersions.forEach(version -> {
            if (project.getVersionId()!=null) {
                if (project.getVersionId().intValue() == version.getId().intValue()) {
                    String envTypes = version.getEnvTypes();
                    StringBuilder envType = new StringBuilder();
                    for (String s : envTypes.split(",")) {
                        envType.append(DOSEnvTypeEnum.getValueByType(Integer.valueOf(s))).append(",");
                    }
                    project.setEnvTypes(version.getEnvTypes());
                    project.setEnvTypeName(envType.substring(0, envType.length() - 1));
                    project.setLatestVersion(version.getVersionCode());
                }
            }
        }));

        dealProjectModules(projectPos);
        List<Integer> projectIds = projectPos.stream().map(DOSProjectPo::getId).collect(Collectors.toList());
        //查询项目授权的环境，集群，命名空间
        List<DOSProjectEvnClusterPo> envClusters = dosProjectEnvClusterNamespaceRelationService.getEnvClusters(projectIds);
        Map<Integer, List<DOSProjectEvnClusterPo>> listMap = envClusters.stream().collect(Collectors.groupingBy(DOSProjectEvnClusterPo::getProjectId));

        projectPos.forEach(project -> {

            if (CollectionUtil.isNotEmpty(listMap.get(project.getId()))){
                StringBuilder uat = new StringBuilder();
                StringBuilder pro = new StringBuilder();
                List<DOSProjectEvnClusterPo> evnClusterList = listMap.get(project.getId());
                for (DOSProjectEvnClusterPo cluster : evnClusterList) {
                    if (cluster.getEnvType().equals(DevOpsConstant.EnvType.UAT.toString())) {
                        uat.append(cluster.getClusterName()).append("/").append(cluster.getNamespace()).append(",");
                    } else {
                        pro.append(cluster.getClusterName()).append("/").append(cluster.getNamespace()).append(",");
                    }
                }
                project.setEnvClusters(evnClusterList);
                if (StrUtil.isNotBlank(uat)) {
                    project.setUat(uat.substring(0, uat.length() - 1));
                }
                if (StrUtil.isNotBlank(pro)) {
                    project.setPro(pro.substring(0, pro.length() - 1));
                }
            }
        });
        Pagination p = Pagination.builder().pageNum(page.getCurrent()).pageSize(page.getSize()).total(page.getTotal()).build();
        tableResult.setPagination(p);
        tableResult.setList(projectPos);
        return tableResult;
    }

    private void dealProjectModules(List<DOSProjectPo> projectPos) {
        if (CollectionUtil.isNotEmpty(projectPos)) {
            List<Integer> projectIds = projectPos.stream().map(DOSProjectPo::getId).collect(Collectors.toList());

            List<DOSProjectModule> modules = dosProjectModuleService
                    .list(Wrappers.<DOSProjectModule>lambdaQuery().in(DOSProjectModule::getProjectId,projectIds));
            Map<Integer, List<DOSProjectModule>> map = modules.stream().collect(Collectors.groupingBy(DOSProjectModule::getProjectId));
            projectPos.forEach(projectPo-> projectPo.setModules(map.get(projectPo.getId())));
        }
    }


    @Override
    public DOSEnvAndGroup getEnvAndGroup() {
        //获取登录的用户信息
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        List<DOSProjectGroup> dosProjectGroups;
        if (user.getAdminRole()) {
            dosProjectGroups = dosProjectGroupMapper.selectList(Wrappers.emptyWrapper());
        } else {
            dosProjectGroups = dosProjectGroupMapper.getProjectGroupByUserid(user.getId());
        }
        //按照groupId进行倒排序
        dosProjectGroups =
                dosProjectGroups.stream().distinct().sorted(Comparator.comparing(DOSProjectGroup::getId).reversed()).collect(Collectors.toList());
        DOSEnvAndGroup group = new DOSEnvAndGroup();
        group.setGroups(dosProjectGroups);
        return group;
    }

    @Override
    public DOSProjectInfoPo getProjectInfo(Integer projectId) {
        Assert.isTrueMvc(projectId != null && projectId > 0,"projectId不能为空");
        //查询项目信息
        DOSProjectPo dosProjectPo = baseMapper.getProjectInfo(projectId);
        Assert.isTrueMvc(dosProjectPo != null && dosProjectPo.getId() != null && dosProjectPo.getId() > 0,"项目信息为空");
        List<DOSProjectPo> projectPos = new ArrayList<>();
        projectPos.add(dosProjectPo);
        //组装项目模块
        dealProjectModules(projectPos);
        //查询release列表
        List<DOSReleasePo> releases = dosReleaseService.getReleasesByProjectId(projectId);
        List<DOSReleaseTypePo> releaseTypePos = new ArrayList<>();
        DOSReleaseTypePo helmRelease = new DOSReleaseTypePo(1,new ArrayList<>());
        DOSReleaseTypePo yamlRelease = new DOSReleaseTypePo(2,new ArrayList<>());
        releaseTypePos.add(helmRelease);
        releaseTypePos.add(yamlRelease);
        if (CollectionUtil.isNotEmpty(releases)){
            List<DOSReleasePo> helmReleases = new ArrayList<>();
            List<DOSReleasePo> yamlReleases = new ArrayList<>();
            for (DOSReleasePo release : releases) {
                if (release.getReleaseType() != null
                        && release.getReleaseType().intValue() == DevOpsConstant.ReleaseType.YAML.intValue()){
                    // 查询release的image:imageTag
                    yamlReleases.add(release);
                }else {
                    helmReleases.add(release);
                }
            }
            // 封装release
            DOSReleaseTypePo yamlTypePo = new DOSReleaseTypePo();
            yamlTypePo.setReleaseType(DevOpsConstant.ReleaseType.YAML);
            yamlTypePo.setReleases(new ArrayList<>());
            releaseTypePos.remove(yamlTypePo);
            yamlReleases.sort(Comparator.comparing(DOSReleasePo::getEnvTypeName).thenComparing(DOSReleasePo::getReleaseName));
            yamlTypePo.setReleases(yamlReleases);
            releaseTypePos.add(yamlTypePo);

            DOSReleaseTypePo helmTypePo = new DOSReleaseTypePo();
            helmTypePo.setReleaseType(DevOpsConstant.ReleaseType.HELM);
            helmTypePo.setReleases(new ArrayList<>());
            releaseTypePos.remove(helmTypePo);
            helmReleases.sort(Comparator.comparing(DOSReleasePo::getEnvTypeName).thenComparing(DOSReleasePo::getReleaseName));
            helmTypePo.setReleases(helmReleases);
            releaseTypePos.add(helmTypePo);
        }
        // 排序
        List<DOSReleaseTypePo> releaseTypes = releaseTypePos.stream()
                .sorted(Comparator.comparing(DOSReleaseTypePo::getReleaseType)).collect(Collectors.toList());
        DOSResourceVo dosResourceVo = DOSResourceVo.builder().projectId(projectId).build();
        //查询资源列表信息
        List<DOSResourcePo> dosResourcePos = dosResourceService.queryResources(dosResourceVo);
        List<DOSResourceTypePo> typePos = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dosResourcePos)) {
            Map<Integer, List<DOSResourcePo>> resourceMap = dosResourcePos.stream().collect(Collectors.groupingBy(DOSResourcePo::getType));
            resourceMap.forEach((k, v) -> {
                DOSResourcePo dosResourcePo = v.get(0);
                DOSResourceTypePo typePo = DOSResourceTypePo.builder()
                        .type(k).resources(v).id(dosResourcePo.getResourceTypeId()).name(dosResourcePo.getTypeName()).build();
                typePos.add(typePo);
            });
        }
        // 查询项目的授权信息
        List<DOSProjectEvnClusterPo> projectEvnClusters = dosProjectEnvClusterNamespaceRelationService.getAuthorizationInfo(projectId);

        return DOSProjectInfoPo.builder()
                .project(projectPos.get(0))
                .releasesPo(releaseTypes)
                .resourcesTypePo(typePos)
                .projectEvnClusters(projectEvnClusters)
                .build();
    }

    @Override
    public List<DOSBindStatusProject> getBindProjects(Integer resourceId) {
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        Assert.isTrue(null!=user,"该用户未认证");
        Assert.isTrue(user.getAdminRole(),"权限不足");

        //查询已绑定和未绑定的项目
        List<DOSProjectPo> projectPos = baseMapper.selectBindProjects(resourceId);
        Map<Integer, List<DOSProjectPo>> projectMap = projectPos.stream().collect(Collectors.groupingBy(DOSProjectPo::getBindStatus));
        List<DOSBindStatusProject> projects = new ArrayList<>();
        DOSBindStatusProject noBindProject = DOSBindStatusProject.builder()
                        .bindStatus(0).projects(new ArrayList<>()).build();
        projects.add(noBindProject);
        DOSBindStatusProject bindProject = DOSBindStatusProject.builder()
                .bindStatus(1).projects(new ArrayList<>()).build();
        projects.add(bindProject);

        projectMap.forEach((k,v) -> {

            DOSBindStatusProject project = new DOSBindStatusProject();
            project.setProjects(new ArrayList<>());
            project.setBindStatus(k);
            projects.remove(project);
            project.setProjects(v);
            projects.add(project);
        });
        List<DOSBindStatusProject> projectList = projects.stream().sorted(Comparator.comparing(DOSBindStatusProject::getBindStatus)).collect(Collectors.toList());
        return projectList;
    }

    @Override
    public Boolean updateProject(DOSUpdateProjectVo project) {
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        //入参校验
        Assert.isTrueMvc(project != null && project.getId() > 0,"项目ID不能为空");
        //操作权限校验
        Integer projectGroupId = getById(project.getId()).getGroupId();
        Assert.isTrueMvc(projectGroupId != null && projectGroupId > 0,"项目不存在");
        checkProjectWritePermission(user, projectGroupId);
        DOSProject byId = this.getById(project.getId());
        if (!byId.getProjectName().equals(project.getProjectName()) && project.getCheckType() == 0) {
            List<String> similarityProjectName = baseMapper.getProjectSimilarity(project.getProjectName(),similarityLimit);
            Assert.isSimilar(ObjectUtil.isEmpty(similarityProjectName), "项目名称与" + StringUtils.join(similarityProjectName, ",") + "相似度较高，是否修改");
        }
        DOSProject dosProject = new DOSProject();
        BeanUtil.copyProperties(project,dosProject);

        return this.updateById(dosProject);
    }

    @Override
    public List<DOSProjectPo> getProjectsByUserId(DOSProjectVo projectVo) {

        return baseMapper.getProjects(projectVo);
    }

    /**
     * @Author heyabo
     * @Description 删除项目下绑定的模块
     * @Date 14:55 2022/3/22
     * @Param [projectId]
     * @return void
     **/
    private void deleteModule(Integer projectId) {
        LambdaQueryWrapper<DOSProjectModule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DOSProjectModule::getProjectId, projectId);
        dosProjectModuleService.remove(queryWrapper);
    }

    /**
     * 检查用户是否有权限操作 创建项目 权限管理 编辑项目 删除项目
     * @param user
     * @param projectGroupId
     */
    public void checkProjectWritePermission(BaseUserVM user,Integer projectGroupId) {
        Assert.isTrueMvc(user != null,"user不能为null");
        Assert.isTrueMvc(projectGroupId != null && projectGroupId > 0,"projectGroupId不能为空");
        List<String> roleCodes = user.getRoles().stream().map(DOSRolePo::getRoleCode).collect(Collectors.toList());
        Assert.isTrueMvc(user.getAdminRole() || roleCodes.contains(DOSRoleEnum.TECHNOLOGY_MANAGER.getRoleCode()),"当前账号无此操作权限");
        if (user.getAdminRole()) {
            //do nothing
        } else if (roleCodes.contains(DOSRoleEnum.TECHNOLOGY_MANAGER.getRoleCode())) {
            DOSProjectGroupUserRelation relation = dosProjectGroupUserRelationService.getOne(Wrappers
                    .<DOSProjectGroupUserRelation>lambdaQuery()
                    .eq(DOSProjectGroupUserRelation::getProjectGroupId,projectGroupId)
                    .eq(DOSProjectGroupUserRelation::getUserType,DevOpsConstant.ProjectGroupUserRelationUserType.PERSON_IN_CHARGE)
                    .eq(DOSProjectGroupUserRelation::getUserId,user.getId()));
            com.devops.admin.util.Assert.isTrue(relation != null && relation.getUserId() != null,"无本项目操作权限");
        }
    }
}
